/*
 * Copyright (c) 2014 pkzd/1.0, 颜文泽(robin)
 * All rights reserved.
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *     1 Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     2 Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include	<asm.h>
#include	<ide.h>
#include	<map.h>
#include	<page.h>
#include	<proc.h>
#include	<string.h>

uint pagettl;    
uint pagecnt;    
uint highpri;   

void
swap(struct proc *p)
{
	uint i, j;
	uint *p0, *p1;

	p0 = p->p_pgdir;
	for(i = PG_KERN_NUM; i < PG_TBL_NUM; i++){
		if(p1 = (uint *)(p0[i] & PG_ADDR_MASK)){
			for(j = 0; j < PG_TBL_NUM; j++){
				if(p1[j] & PG_P){
					if(p1[j] & PG_A){
						p1[j] &= ~PG_A;
						continue;
					}
					if(p1[j] & PG_M){
						p1[j] &= ~PG_M;
						continue;
					}
					swapout(i * KPAGESIZ + j * PAGESIZ, &p1[j]);
				}
			}
		}	
	}
}

int
swapout(uint addr, uint *pte)
{
	uint x, n;
	struct page_list *p;

	x = *pte;
	if(x & PG_SWP)
		return 0;
	if(!(p = page_list_get(PAGE_LIST_ADDR(x))) || p->p_cnt > 1)
		return 0;
	if(!(n = (uint)malloc(swapmap, 1))) 
		return -1;
	if(raw_write(n, addr) == -1){
		mfree(swapmap, 1, n);
		return -1;
	}
	mfree(coremap, 1, p->p_addr);
	page_list_free(p->p_addr);
	*pte = (n << PG_PTE_SHIFT) | PG_SWP;
	page_list_alloc(n | PAGE_LIST_SWP);
	return 0;
}

int
swapin(uint addr, uint *pte)
{
	uint x, n;
	char buf[PAGESIZ];
	struct page_list *p;

	x = *pte;
	p = page_list_get(PAGE_LIST_ADDR(x));
	if(p == NULL || p->p_cnt == 0)
		return -1;
	if(x & PG_SWP){	
		if(!(n = (uint)malloc(coremap, 1)))
                                return -1;
                *pte = n | PG_P | PG_W | PG_U;
		flush_tbl();
                if(raw_read(p->p_addr & ~PAGE_LIST_SWP, addr) == -1){
                	*pte = x;
                 	mfree(coremap, 1, n);
			flush_tbl();
               		return -1;
              	}
		if(p->p_cnt == 1) 
			mfree(swapmap, 1, p->p_addr & ~PAGE_LIST_SWP);
                page_list_free(p->p_addr);
                page_list_alloc(n);
		return 0;
	}
	if(p->p_cnt == 1){ 
		*pte = p->p_addr | PG_P | PG_W | PG_U;
		flush_tbl();
		return 0;
	}
	if(!(n = (uint)malloc(coremap, 1)))
		return -1;
	*pte = p->p_addr | PG_P | PG_W | PG_U;
	flush_tbl();
	memmove(buf, (char *)addr, PAGESIZ);
	page_list_free(p->p_addr);
	*pte = n | PG_P | PG_W | PG_U;
	flush_tbl();
	memmove((char *)addr, buf, PAGESIZ);
	page_list_alloc(n);
	return 0;
}

int
raw_write(uint bn, uint buf)
{
	char *p;
	uint cnt;

	cnt = PAGESIZ / SECTORSIZ;
	while((inb(ide_cmd + 7) & IDE_MASK) != IDE_DRDY)
		;
	outb(cnt, ide_cmd + 2);
	outb((uchar)bn, ide_cmd + 3);
	outb((uchar)(bn >> 8), ide_cmd + 4);
	outb((uchar)(bn >> 16), ide_cmd + 5);
	outb((uchar)((bn >> 24) | 0xe0 | ide_slv), ide_cmd + 6); 
	outb(IDE_WRITE, ide_cmd + 7);
	p = (char *)buf;
loop:
	while(inb(ide_cmd + 7) & IDE_BUSY)
		;
	if(inb(ide_cmd + 7) & IDE_ERR)
		return -1;
	if(inb(ide_cmd + 7) & IDE_DRQ){
		cnt--;
		outsw(p, ide_cmd, SECTORSIZ >> 1);
		p += SECTORSIZ;
	}
	if(cnt)
		goto loop;
	return 0;
}

int
raw_read(uint bn, uint buf)
{
        char *p;
        uint cnt;

	cnt = PAGESIZ / SECTORSIZ;
        while((inb(ide_cmd + 7) & IDE_MASK) != IDE_DRDY)
                ;
        outb(cnt, ide_cmd + 2);
        outb((uchar)bn, ide_cmd + 3);
        outb((uchar)(bn >> 8), ide_cmd + 4);
        outb((uchar)(bn >> 16), ide_cmd + 5);
        outb((uchar)((bn >> 24) | 0xe0 | ide_slv), ide_cmd + 6);
        outb(IDE_READ, ide_cmd + 7);
        p = (char *)buf;
loop:
        while(inb(ide_cmd + 7) & IDE_BUSY)
                ;
        if(inb(ide_cmd + 7) & IDE_ERR)
                return -1;
        if(inb(ide_cmd + 7) & IDE_DRQ){
                cnt--;
                insw(p, ide_cmd, SECTORSIZ >> 1);
                p += SECTORSIZ;
        }
        if(cnt)
                goto loop;
        return 0;
}
